﻿using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace GX
{
    public class ObjectPool : IObjectPool
    {
        static readonly List<ObjectPool> CACHE = new List<ObjectPool>(128);
        static public ObjectPool GetTemporary(int capacity=0)
        {
            if(CACHE.Count == 0)
                return new ObjectPool();
            ObjectPool pool = CACHE[CACHE.Count-1];
            CACHE.RemoveAt(CACHE.Count -1 );
            pool._enabled = true;
            return pool;
        }
        static public void ReleaseTemporary(ObjectPool pool)
        {
            if (!pool._enabled)
                return;
            pool._enabled = false;
            pool.GC();
            if (CACHE.Count < CACHE.Capacity)
                CACHE.Add(pool);
        }

        bool _enabled = true;
        int _capacity;
        int _totalCount = 0;
        Stack<object> _stack = new Stack<object>(16);

        private ObjectPool() { }
        public int deactiveCount { get { return _stack.Count; } }
        public int activeCount { get { return _totalCount > _stack.Count ? _totalCount-_stack.Count : 0; } }
        public int totalCount { get { return _totalCount; } }
        public int capacity
        {
            get { return _capacity; }
            set
            {
                if (value < 0)
                    _capacity = 0;
                else
                    _capacity = value;
            }
        }
        public bool enabled{get { return _enabled; }}
        public PoolDelegate onGet { internal get; set; }
        public PoolDelegate onRelease { internal get; set; }
        void OnGet(object item, bool isNew)
        {
            if (isNew)
                ++_totalCount;
            if (item is IPoolable)
                (item as IPoolable).OnReuse(isNew);

            if (null != onGet)
                onGet(item, isNew);
        }
        void OnRelease(object item, bool isDestroy)
        {
            if (isDestroy && _totalCount > 0)
                --_totalCount;

            if (item is IPoolable)
                (item as IPoolable).OnRecycle(isDestroy);
            else if (item is IList)
                (item as IList).Clear();
            else if (item is IDictionary)
                (item as IDictionary).Clear();
            else if (item is StringBuilder)
                (item as StringBuilder).Length = 0;
            
            if (null != onRelease)
                onRelease(item,isDestroy);
        }

        public void Put(object item)
        {
            if (ReferenceEquals(_stack.Peek(), item))
                return;
            _stack.Push(item);
            ++_totalCount;

        }
        public object OnCreate(object item)
        {
            OnGet(item, true);
            return item;
        }
        public bool Get(out object item)
        {
            if (_stack.Count == 0)
            {
                item = null;
                return false;
            }
            item = _stack.Pop();
            OnGet(item,false);
            return true;
        }
        public object Get()
        {
            object item;
            Get(out item);
            return item;
        }
        public void Release(object item)
        {
            if (_stack.Count != 0 && ReferenceEquals(_stack.Peek(), item))
                return;
            _stack.Push(item);
            OnRelease(item,false);
        }
        public void GC()
        {
            if (_capacity == 0)
                return;
            object item;
            while (_stack.Count > _capacity)
            {
                item = _stack.Pop();
                OnRelease(item,true);
            }
        }
        public void Clear()
        {
            while (_stack.Count != 0)
            {
                var item = _stack.Pop();
                OnRelease(item, true);
            }
        }
    }
}